package com.brzjomo.jomosadventure.block;

import net.minecraft.block.*;
import net.minecraft.block.enums.BlockHalf;
import net.minecraft.block.enums.StairShape;
import net.minecraft.entity.Entity;
import net.minecraft.entity.ai.pathing.NavigationType;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.fluid.FluidState;
import net.minecraft.fluid.Fluids;
import net.minecraft.item.ItemPlacementContext;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.state.StateManager;
import net.minecraft.state.property.*;
import net.minecraft.util.ActionResult;
import net.minecraft.util.BlockMirror;
import net.minecraft.util.BlockRotation;
import net.minecraft.util.Hand;
import net.minecraft.util.function.BooleanBiFunction;
import net.minecraft.util.hit.BlockHitResult;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import net.minecraft.util.shape.VoxelShape;
import net.minecraft.util.shape.VoxelShapes;
import net.minecraft.world.BlockView;
import net.minecraft.world.World;
import net.minecraft.world.WorldAccess;
import net.minecraft.world.explosion.Explosion;

import java.util.Random;
import java.util.stream.Stream;

public class SlopeBlock extends Block implements Waterloggable {
    public static final DirectionProperty FACING;
    public static final EnumProperty<BlockHalf> HALF;
    public static final EnumProperty<StairShape> SHAPE;
    public static final BooleanProperty WATERLOGGED;
    private final Block baseBlock;
    private final BlockState baseBlockState;

    public SlopeBlock(BlockState baseBlockState, Settings settings) {
        super(settings);
        this.setDefaultState(this.stateManager.getDefaultState().with(FACING, Direction.NORTH).with(HALF, BlockHalf.BOTTOM).with(SHAPE, StairShape.STRAIGHT).with(WATERLOGGED, false));
        this.baseBlock = baseBlockState.getBlock();
        this.baseBlockState = baseBlockState;
    }

    public boolean hasSidedTransparency(BlockState state) {
        return true;
    }

    public VoxelShape getOutlineShape(BlockState state, BlockView world, BlockPos pos, ShapeContext context) {
        if (state.get(HALF) == BlockHalf.TOP) {
            if (state.get(SHAPE) == StairShape.STRAIGHT) {
                switch (state.get(FACING)) {
                    case NORTH:
                    default:
                        return SHAPE_N_T;
                    case SOUTH:
                        return SHAPE_S_T;
                    case EAST:
                        return SHAPE_E_T;
                    case WEST:
                        return SHAPE_W_T;
                }
            } else if (state.get(SHAPE) == StairShape.OUTER_LEFT) {
                switch (state.get(FACING)) {
                    case NORTH:
                    default:
                        return SHAPE_NW_T;
                    case SOUTH:
                        return SHAPE_SE_T;
                    case EAST:
                        return SHAPE_NE_T;
                    case WEST:
                        return SHAPE_SW_T;
                }
            } else if (state.get(SHAPE) == StairShape.OUTER_RIGHT) {
                switch (state.get(FACING)) {
                    case NORTH:
                    default:
                        return SHAPE_NE_T;
                    case SOUTH:
                        return SHAPE_SW_T;
                    case EAST:
                        return SHAPE_SE_T;
                    case WEST:
                        return SHAPE_NW_T;
                }
            } else if (state.get(SHAPE) == StairShape.INNER_LEFT) {
                switch (state.get(FACING)) {
                    case NORTH:
                    default:
                        return SHAPE_NW_I_T;
                    case SOUTH:
                        return SHAPE_SE_I_T;
                    case EAST:
                        return SHAPE_NE_I_T;
                    case WEST:
                        return SHAPE_SW_I_T;
                }
            } else {
                switch (state.get(FACING)) {
                    case NORTH:
                    default:
                        return SHAPE_NE_I_T;
                    case SOUTH:
                        return SHAPE_SW_I_T;
                    case EAST:
                        return SHAPE_SE_I_T;
                    case WEST:
                        return SHAPE_NW_I_T;
                }
            }
        } else {
            if (state.get(SHAPE) == StairShape.STRAIGHT) {
                switch (state.get(FACING)) {
                    case NORTH:
                    default:
                        return SHAPE_N;
                    case SOUTH:
                        return SHAPE_S;
                    case EAST:
                        return SHAPE_E;
                    case WEST:
                        return SHAPE_W;
                }
            } else if (state.get(SHAPE) == StairShape.OUTER_LEFT) {
                switch (state.get(FACING)) {
                    case NORTH:
                    default:
                        return SHAPE_NW;
                    case SOUTH:
                        return SHAPE_SE;
                    case EAST:
                        return SHAPE_NE;
                    case WEST:
                        return SHAPE_SW;
                }
            } else if (state.get(SHAPE) == StairShape.OUTER_RIGHT) {
                switch (state.get(FACING)) {
                    case NORTH:
                    default:
                        return SHAPE_NE;
                    case SOUTH:
                        return SHAPE_SW;
                    case EAST:
                        return SHAPE_SE;
                    case WEST:
                        return SHAPE_NW;
                }
            } else if (state.get(SHAPE) == StairShape.INNER_LEFT) {
                switch (state.get(FACING)) {
                    case NORTH:
                    default:
                        return SHAPE_NW_I;
                    case SOUTH:
                        return SHAPE_SE_I;
                    case EAST:
                        return SHAPE_NE_I;
                    case WEST:
                        return SHAPE_SW_I;
                }
            } else {
                switch (state.get(FACING)) {
                    case NORTH:
                    default:
                        return SHAPE_NE_I;
                    case SOUTH:
                        return SHAPE_SW_I;
                    case EAST:
                        return SHAPE_SE_I;
                    case WEST:
                        return SHAPE_NW_I;
                }
            }
        }

    }

    public void randomDisplayTick(BlockState state, World world, BlockPos pos, Random random) {
        this.baseBlock.randomDisplayTick(state, world, pos, random);
    }

    public void onBlockBreakStart(BlockState state, World world, BlockPos pos, PlayerEntity player) {
        this.baseBlockState.onBlockBreakStart(world, pos, player);
    }

    public void onBroken(WorldAccess world, BlockPos pos, BlockState state) {
        this.baseBlock.onBroken(world, pos, state);
    }

    public float getBlastResistance() {
        return this.baseBlock.getBlastResistance();
    }

    public void onBlockAdded(BlockState state, World world, BlockPos pos, BlockState oldState, boolean notify) {
        if (!state.isOf(state.getBlock())) {
            this.baseBlockState.neighborUpdate(world, pos, Blocks.AIR, pos, false);
            this.baseBlock.onBlockAdded(this.baseBlockState, world, pos, oldState, false);
        }
    }

    public void onStateReplaced(BlockState state, World world, BlockPos pos, BlockState newState, boolean moved) {
        if (!state.isOf(newState.getBlock())) {
            this.baseBlockState.onStateReplaced(world, pos, newState, moved);
        }
    }

    public void onSteppedOn(World world, BlockPos pos, BlockState state, Entity entity) {
        this.baseBlock.onSteppedOn(world, pos, state, entity);
    }

    public boolean hasRandomTicks(BlockState state) {
        return this.baseBlock.hasRandomTicks(state);
    }

    public void randomTick(BlockState state, ServerWorld world, BlockPos pos, Random random) {
        this.baseBlock.randomTick(state, world, pos, random);
    }

    public void scheduledTick(BlockState state, ServerWorld world, BlockPos pos, Random random) {
        this.baseBlock.scheduledTick(state, world, pos, random);
    }

    public ActionResult onUse(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockHitResult hit) {
        return this.baseBlockState.onUse(world, player, hand, hit);
    }

    public void onDestroyedByExplosion(World world, BlockPos pos, Explosion explosion) {
        this.baseBlock.onDestroyedByExplosion(world, pos, explosion);
    }

    public BlockState getPlacementState(ItemPlacementContext ctx) {
        Direction direction = ctx.getSide();
        BlockPos blockPos = ctx.getBlockPos();
        FluidState fluidState = ctx.getWorld().getFluidState(blockPos);
        BlockState blockState = this.getDefaultState().with(FACING, ctx.getPlayerFacing().getOpposite()).with(HALF, direction != Direction.DOWN && (direction == Direction.UP || !(ctx.getHitPos().y - (double) blockPos.getY() > 0.5D)) ? BlockHalf.BOTTOM : BlockHalf.TOP).with(WATERLOGGED, fluidState.getFluid() == Fluids.WATER);
        return (BlockState) blockState.with(SHAPE, getSlopeShape(blockState, ctx.getWorld(), blockPos));
    }

    public BlockState getStateForNeighborUpdate(BlockState state, Direction direction, BlockState neighborState, WorldAccess world, BlockPos pos, BlockPos neighborPos) {
        if ((Boolean) state.get(WATERLOGGED)) {
            world.getFluidTickScheduler().schedule(pos, Fluids.WATER, Fluids.WATER.getTickRate(world));
        }

        return direction.getAxis().isHorizontal() ? state.with(SHAPE, getSlopeShape(state, world, pos)) : super.getStateForNeighborUpdate(state, direction, neighborState, world, pos, neighborPos);
    }

    private static StairShape getSlopeShape(BlockState state, BlockView world, BlockPos pos) {
        Direction direction = (Direction) state.get(FACING);
        BlockState blockState = world.getBlockState(pos.offset(direction));
        if (isSlope(blockState) && state.get(HALF) == blockState.get(HALF)) {
            Direction direction2 = (Direction) blockState.get(FACING);
            if (direction2.getAxis() != ((Direction) state.get(FACING)).getAxis() && isDifferentOrientation(state, world, pos, direction2.getOpposite())) {
                if (direction2 == direction.rotateYCounterclockwise()) {
                    return StairShape.INNER_LEFT;
                }

                return StairShape.INNER_RIGHT;
            }
        }

        BlockState blockState2 = world.getBlockState(pos.offset(direction.getOpposite()));
        if (isSlope(blockState2) && state.get(HALF) == blockState2.get(HALF)) {
            Direction direction3 = (Direction) blockState2.get(FACING);
            if (direction3.getAxis() != ((Direction) state.get(FACING)).getAxis() && isDifferentOrientation(state, world, pos, direction3)) {
                if (direction3 == direction.rotateYCounterclockwise()) {
                    return StairShape.OUTER_LEFT;
                }

                return StairShape.OUTER_RIGHT;
            }
        }

        return StairShape.STRAIGHT;
    }

    private static boolean isDifferentOrientation(BlockState state, BlockView world, BlockPos pos, Direction dir) {
        BlockState blockState = world.getBlockState(pos.offset(dir));
        return !isSlope(blockState) || blockState.get(FACING) != state.get(FACING) || blockState.get(HALF) != state.get(HALF);
    }

    public static boolean isSlope(BlockState state) {
        return state.getBlock() instanceof SlopeBlock;
    }

    public BlockState rotate(BlockState state, BlockRotation rotation) {
        return state.with(FACING, rotation.rotate(state.get(FACING)));
    }

    public BlockState mirror(BlockState state, BlockMirror mirror) {
        Direction direction = (Direction) state.get(FACING);
        StairShape stairShape = (StairShape) state.get(SHAPE);
        switch (mirror) {
            case LEFT_RIGHT:
                if (direction.getAxis() == Direction.Axis.Z) {
                    switch (stairShape) {
                        case INNER_LEFT:
                            return (BlockState) state.rotate(BlockRotation.CLOCKWISE_180).with(SHAPE, StairShape.INNER_RIGHT);
                        case INNER_RIGHT:
                            return (BlockState) state.rotate(BlockRotation.CLOCKWISE_180).with(SHAPE, StairShape.INNER_LEFT);
                        case OUTER_LEFT:
                            return (BlockState) state.rotate(BlockRotation.CLOCKWISE_180).with(SHAPE, StairShape.OUTER_RIGHT);
                        case OUTER_RIGHT:
                            return (BlockState) state.rotate(BlockRotation.CLOCKWISE_180).with(SHAPE, StairShape.OUTER_LEFT);
                        default:
                            return state.rotate(BlockRotation.CLOCKWISE_180);
                    }
                }
                break;
            case FRONT_BACK:
                if (direction.getAxis() == Direction.Axis.X) {
                    switch (stairShape) {
                        case INNER_LEFT:
                            return (BlockState) state.rotate(BlockRotation.CLOCKWISE_180).with(SHAPE, StairShape.INNER_LEFT);
                        case INNER_RIGHT:
                            return (BlockState) state.rotate(BlockRotation.CLOCKWISE_180).with(SHAPE, StairShape.INNER_RIGHT);
                        case OUTER_LEFT:
                            return (BlockState) state.rotate(BlockRotation.CLOCKWISE_180).with(SHAPE, StairShape.OUTER_RIGHT);
                        case OUTER_RIGHT:
                            return (BlockState) state.rotate(BlockRotation.CLOCKWISE_180).with(SHAPE, StairShape.OUTER_LEFT);
                        case STRAIGHT:
                            return state.rotate(BlockRotation.CLOCKWISE_180);
                    }
                }
        }

        return super.mirror(state, mirror);
    }

    protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
        builder.add(new Property[]{FACING, HALF, SHAPE, WATERLOGGED});
    }

    public FluidState getFluidState(BlockState state) {
        return (Boolean) state.get(WATERLOGGED) ? Fluids.WATER.getStill(false) : super.getFluidState(state);
    }

    public boolean canPathfindThrough(BlockState state, BlockView world, BlockPos pos, NavigationType type) {
        return false;
    }

    static {
        FACING = HorizontalFacingBlock.FACING;
        HALF = Properties.BLOCK_HALF;
        SHAPE = Properties.STAIR_SHAPE;
        WATERLOGGED = Properties.WATERLOGGED;
    }

    protected static final VoxelShape SHAPE_N = Stream.of(
            Block.createCuboidShape(0, 0, 0, 16, 1, 16),
            Block.createCuboidShape(0, 1, 1, 16, 2, 16),
            Block.createCuboidShape(0, 2, 2, 16, 3, 16),
            Block.createCuboidShape(0, 3, 3, 16, 4, 16),
            Block.createCuboidShape(0, 4, 4, 16, 5, 16),
            Block.createCuboidShape(0, 5, 5, 16, 6, 16),
            Block.createCuboidShape(0, 6, 6, 16, 7, 16),
            Block.createCuboidShape(0, 7, 7, 16, 8, 16),
            Block.createCuboidShape(0, 8, 8, 16, 9, 16),
            Block.createCuboidShape(0, 9, 9, 16, 10, 16),
            Block.createCuboidShape(0, 10, 10, 16, 11, 16),
            Block.createCuboidShape(0, 11, 11, 16, 12, 16),
            Block.createCuboidShape(0, 12, 12, 16, 13, 16),
            Block.createCuboidShape(0, 13, 13, 16, 14, 16),
            Block.createCuboidShape(0, 14, 14, 16, 15, 16),
            Block.createCuboidShape(0, 15, 15, 16, 16, 16)
    ).reduce((v1, v2) -> VoxelShapes.combineAndSimplify(v1, v2, BooleanBiFunction.OR)).get();
    protected static final VoxelShape SHAPE_W = Stream.of(
            Block.createCuboidShape(0, 0, 0, 16, 1, 16),
            Block.createCuboidShape(1, 1, 0, 16, 2, 16),
            Block.createCuboidShape(2, 2, 0, 16, 3, 16),
            Block.createCuboidShape(3, 3, 0, 16, 4, 16),
            Block.createCuboidShape(4, 4, 0, 16, 5, 16),
            Block.createCuboidShape(5, 5, 0, 16, 6, 16),
            Block.createCuboidShape(6, 6, 0, 16, 7, 16),
            Block.createCuboidShape(7, 7, 0, 16, 8, 16),
            Block.createCuboidShape(8, 8, 0, 16, 9, 16),
            Block.createCuboidShape(9, 9, 0, 16, 10, 16),
            Block.createCuboidShape(10, 10, 0, 16, 11, 16),
            Block.createCuboidShape(11, 11, 0, 16, 12, 16),
            Block.createCuboidShape(12, 12, 0, 16, 13, 16),
            Block.createCuboidShape(13, 13, 0, 16, 14, 16),
            Block.createCuboidShape(14, 14, 0, 16, 15, 16),
            Block.createCuboidShape(15, 15, 0, 16, 16, 16)
    ).reduce((v1, v2) -> VoxelShapes.combineAndSimplify(v1, v2, BooleanBiFunction.OR)).get();
    protected static final VoxelShape SHAPE_S = Stream.of(
            Block.createCuboidShape(0, 0, 0, 16, 1, 16),
            Block.createCuboidShape(0, 1, 0, 16, 2, 15),
            Block.createCuboidShape(0, 2, 0, 16, 3, 14),
            Block.createCuboidShape(0, 3, 0, 16, 4, 13),
            Block.createCuboidShape(0, 4, 0, 16, 5, 12),
            Block.createCuboidShape(0, 5, 0, 16, 6, 11),
            Block.createCuboidShape(0, 6, 0, 16, 7, 10),
            Block.createCuboidShape(0, 7, 0, 16, 8, 9),
            Block.createCuboidShape(0, 8, 0, 16, 9, 8),
            Block.createCuboidShape(0, 9, 0, 16, 10, 7),
            Block.createCuboidShape(0, 10, 0, 16, 11, 6),
            Block.createCuboidShape(0, 11, 0, 16, 12, 5),
            Block.createCuboidShape(0, 12, 0, 16, 13, 4),
            Block.createCuboidShape(0, 13, 0, 16, 14, 3),
            Block.createCuboidShape(0, 14, 0, 16, 15, 2),
            Block.createCuboidShape(0, 15, 0, 16, 16, 1)
    ).reduce((v1, v2) -> VoxelShapes.combineAndSimplify(v1, v2, BooleanBiFunction.OR)).get();
    protected static final VoxelShape SHAPE_E = Stream.of(
            Block.createCuboidShape(0, 0, 0, 16, 1, 16),
            Block.createCuboidShape(0, 1, 0, 15, 2, 16),
            Block.createCuboidShape(0, 2, 0, 14, 3, 16),
            Block.createCuboidShape(0, 3, 0, 13, 4, 16),
            Block.createCuboidShape(0, 4, 0, 12, 5, 16),
            Block.createCuboidShape(0, 5, 0, 11, 6, 16),
            Block.createCuboidShape(0, 6, 0, 10, 7, 16),
            Block.createCuboidShape(0, 7, 0, 9, 8, 16),
            Block.createCuboidShape(0, 8, 0, 8, 9, 16),
            Block.createCuboidShape(0, 9, 0, 7, 10, 16),
            Block.createCuboidShape(0, 10, 0, 6, 11, 16),
            Block.createCuboidShape(0, 11, 0, 5, 12, 16),
            Block.createCuboidShape(0, 12, 0, 4, 13, 16),
            Block.createCuboidShape(0, 13, 0, 3, 14, 16),
            Block.createCuboidShape(0, 14, 0, 2, 15, 16),
            Block.createCuboidShape(0, 15, 0, 1, 16, 16)
    ).reduce((v1, v2) -> VoxelShapes.combineAndSimplify(v1, v2, BooleanBiFunction.OR)).get();
    protected static final VoxelShape SHAPE_N_T = Stream.of(
            Block.createCuboidShape(0, 15, 0, 16, 16, 16),
            Block.createCuboidShape(0, 14, 1, 16, 15, 16),
            Block.createCuboidShape(0, 13, 2, 16, 14, 16),
            Block.createCuboidShape(0, 12, 3, 16, 13, 16),
            Block.createCuboidShape(0, 11, 4, 16, 12, 16),
            Block.createCuboidShape(0, 10, 5, 16, 11, 16),
            Block.createCuboidShape(0, 9, 6, 16, 10, 16),
            Block.createCuboidShape(0, 8, 7, 16, 9, 16),
            Block.createCuboidShape(0, 7, 8, 16, 8, 16),
            Block.createCuboidShape(0, 6, 9, 16, 7, 16),
            Block.createCuboidShape(0, 5, 10, 16, 6, 16),
            Block.createCuboidShape(0, 4, 11, 16, 5, 16),
            Block.createCuboidShape(0, 3, 12, 16, 4, 16),
            Block.createCuboidShape(0, 2, 13, 16, 3, 16),
            Block.createCuboidShape(0, 1, 14, 16, 2, 16),
            Block.createCuboidShape(0, 0, 15, 16, 1, 16)
    ).reduce((v1, v2) -> VoxelShapes.combineAndSimplify(v1, v2, BooleanBiFunction.OR)).get();
    protected static final VoxelShape SHAPE_W_T = Stream.of(
            Block.createCuboidShape(0, 15, 0, 16, 16, 16),
            Block.createCuboidShape(1, 14, 0, 16, 15, 16),
            Block.createCuboidShape(2, 13, 0, 16, 14, 16),
            Block.createCuboidShape(3, 12, 0, 16, 13, 16),
            Block.createCuboidShape(4, 11, 0, 16, 12, 16),
            Block.createCuboidShape(5, 10, 0, 16, 11, 16),
            Block.createCuboidShape(6, 9, 0, 16, 10, 16),
            Block.createCuboidShape(7, 8, 0, 16, 9, 16),
            Block.createCuboidShape(8, 7, 0, 16, 8, 16),
            Block.createCuboidShape(9, 6, 0, 16, 7, 16),
            Block.createCuboidShape(10, 5, 0, 16, 6, 16),
            Block.createCuboidShape(11, 4, 0, 16, 5, 16),
            Block.createCuboidShape(12, 3, 0, 16, 4, 16),
            Block.createCuboidShape(13, 2, 0, 16, 3, 16),
            Block.createCuboidShape(14, 1, 0, 16, 2, 16),
            Block.createCuboidShape(15, 0, 0, 16, 1, 16)
    ).reduce((v1, v2) -> VoxelShapes.combineAndSimplify(v1, v2, BooleanBiFunction.OR)).get();
    protected static final VoxelShape SHAPE_S_T = Stream.of(
            Block.createCuboidShape(0, 15, 0, 16, 16, 16),
            Block.createCuboidShape(0, 14, 0, 16, 15, 15),
            Block.createCuboidShape(0, 13, 0, 16, 14, 14),
            Block.createCuboidShape(0, 12, 0, 16, 13, 13),
            Block.createCuboidShape(0, 11, 0, 16, 12, 12),
            Block.createCuboidShape(0, 10, 0, 16, 11, 11),
            Block.createCuboidShape(0, 9, 0, 16, 10, 10),
            Block.createCuboidShape(0, 8, 0, 16, 9, 9),
            Block.createCuboidShape(0, 7, 0, 16, 8, 8),
            Block.createCuboidShape(0, 6, 0, 16, 7, 7),
            Block.createCuboidShape(0, 5, 0, 16, 6, 6),
            Block.createCuboidShape(0, 4, 0, 16, 5, 5),
            Block.createCuboidShape(0, 3, 0, 16, 4, 4),
            Block.createCuboidShape(0, 2, 0, 16, 3, 3),
            Block.createCuboidShape(0, 1, 0, 16, 2, 2),
            Block.createCuboidShape(0, 0, 0, 16, 1, 1)
    ).reduce((v1, v2) -> VoxelShapes.combineAndSimplify(v1, v2, BooleanBiFunction.OR)).get();
    protected static final VoxelShape SHAPE_E_T = Stream.of(
            Block.createCuboidShape(0, 15, 0, 16, 16, 16),
            Block.createCuboidShape(0, 14, 0, 15, 15, 16),
            Block.createCuboidShape(0, 13, 0, 14, 14, 16),
            Block.createCuboidShape(0, 12, 0, 13, 13, 16),
            Block.createCuboidShape(0, 11, 0, 12, 12, 16),
            Block.createCuboidShape(0, 10, 0, 11, 11, 16),
            Block.createCuboidShape(0, 9, 0, 10, 10, 16),
            Block.createCuboidShape(0, 8, 0, 9, 9, 16),
            Block.createCuboidShape(0, 7, 0, 8, 8, 16),
            Block.createCuboidShape(0, 6, 0, 7, 7, 16),
            Block.createCuboidShape(0, 5, 0, 6, 6, 16),
            Block.createCuboidShape(0, 4, 0, 5, 5, 16),
            Block.createCuboidShape(0, 3, 0, 4, 4, 16),
            Block.createCuboidShape(0, 2, 0, 3, 3, 16),
            Block.createCuboidShape(0, 1, 0, 2, 2, 16),
            Block.createCuboidShape(0, 0, 0, 1, 1, 16)
    ).reduce((v1, v2) -> VoxelShapes.combineAndSimplify(v1, v2, BooleanBiFunction.OR)).get();
    protected static final VoxelShape SHAPE_NW = Stream.of(
            Block.createCuboidShape(0, 0, 0, 16, 1, 16),
            Block.createCuboidShape(1, 1, 1, 16, 2, 16),
            Block.createCuboidShape(2, 2, 2, 16, 3, 16),
            Block.createCuboidShape(3, 3, 3, 16, 4, 16),
            Block.createCuboidShape(4, 4, 4, 16, 5, 16),
            Block.createCuboidShape(5, 5, 5, 16, 6, 16),
            Block.createCuboidShape(6, 6, 6, 16, 7, 16),
            Block.createCuboidShape(7, 7, 7, 16, 8, 16),
            Block.createCuboidShape(8, 8, 8, 16, 9, 16),
            Block.createCuboidShape(9, 9, 9, 16, 10, 16),
            Block.createCuboidShape(10, 10, 10, 16, 11, 16),
            Block.createCuboidShape(11, 11, 11, 16, 12, 16),
            Block.createCuboidShape(12, 12, 12, 16, 13, 16),
            Block.createCuboidShape(13, 13, 13, 16, 14, 16),
            Block.createCuboidShape(14, 14, 14, 16, 15, 16),
            Block.createCuboidShape(15, 15, 15, 16, 16, 16)
    ).reduce((v1, v2) -> VoxelShapes.combineAndSimplify(v1, v2, BooleanBiFunction.OR)).get();
    protected static final VoxelShape SHAPE_NE = Stream.of(
            Block.createCuboidShape(0, 0, 0, 16, 1, 16),
            Block.createCuboidShape(0, 1, 1, 15, 2, 16),
            Block.createCuboidShape(0, 2, 2, 14, 3, 16),
            Block.createCuboidShape(0, 3, 3, 13, 4, 16),
            Block.createCuboidShape(0, 4, 4, 12, 5, 16),
            Block.createCuboidShape(0, 5, 5, 11, 6, 16),
            Block.createCuboidShape(0, 6, 6, 10, 7, 16),
            Block.createCuboidShape(0, 7, 7, 9, 8, 16),
            Block.createCuboidShape(0, 8, 8, 8, 9, 16),
            Block.createCuboidShape(0, 9, 9, 7, 10, 16),
            Block.createCuboidShape(0, 10, 10, 6, 11, 16),
            Block.createCuboidShape(0, 11, 11, 5, 12, 16),
            Block.createCuboidShape(0, 12, 12, 4, 13, 16),
            Block.createCuboidShape(0, 13, 13, 3, 14, 16),
            Block.createCuboidShape(0, 14, 14, 2, 15, 16),
            Block.createCuboidShape(0, 15, 15, 1, 16, 16)
    ).reduce((v1, v2) -> VoxelShapes.combineAndSimplify(v1, v2, BooleanBiFunction.OR)).get();
    protected static final VoxelShape SHAPE_SW = Stream.of(
            Block.createCuboidShape(0, 0, 0, 16, 1, 16),
            Block.createCuboidShape(1, 1, 0, 16, 2, 15),
            Block.createCuboidShape(2, 2, 0, 16, 3, 14),
            Block.createCuboidShape(3, 3, 0, 16, 4, 13),
            Block.createCuboidShape(4, 4, 0, 16, 5, 12),
            Block.createCuboidShape(5, 5, 0, 16, 6, 11),
            Block.createCuboidShape(6, 6, 0, 16, 7, 10),
            Block.createCuboidShape(7, 7, 0, 16, 8, 9),
            Block.createCuboidShape(8, 8, 0, 16, 9, 8),
            Block.createCuboidShape(9, 9, 0, 16, 10, 7),
            Block.createCuboidShape(10, 10, 0, 16, 11, 6),
            Block.createCuboidShape(11, 11, 0, 16, 12, 5),
            Block.createCuboidShape(12, 12, 0, 16, 13, 4),
            Block.createCuboidShape(13, 13, 0, 16, 14, 3),
            Block.createCuboidShape(14, 14, 0, 16, 15, 2),
            Block.createCuboidShape(15, 15, 0, 16, 16, 1)
    ).reduce((v1, v2) -> VoxelShapes.combineAndSimplify(v1, v2, BooleanBiFunction.OR)).get();
    protected static final VoxelShape SHAPE_SE = Stream.of(
            Block.createCuboidShape(0, 0, 0, 16, 1, 16),
            Block.createCuboidShape(0, 1, 0, 15, 2, 15),
            Block.createCuboidShape(0, 2, 0, 14, 3, 14),
            Block.createCuboidShape(0, 3, 0, 13, 4, 13),
            Block.createCuboidShape(0, 4, 0, 12, 5, 12),
            Block.createCuboidShape(0, 5, 0, 11, 6, 11),
            Block.createCuboidShape(0, 6, 0, 10, 7, 10),
            Block.createCuboidShape(0, 7, 0, 9, 8, 9),
            Block.createCuboidShape(0, 8, 0, 8, 9, 8),
            Block.createCuboidShape(0, 9, 0, 7, 10, 7),
            Block.createCuboidShape(0, 10, 0, 6, 11, 6),
            Block.createCuboidShape(0, 11, 0, 5, 12, 5),
            Block.createCuboidShape(0, 12, 0, 4, 13, 4),
            Block.createCuboidShape(0, 13, 0, 3, 14, 3),
            Block.createCuboidShape(0, 14, 0, 2, 15, 2),
            Block.createCuboidShape(0, 15, 0, 1, 16, 1)
    ).reduce((v1, v2) -> VoxelShapes.combineAndSimplify(v1, v2, BooleanBiFunction.OR)).get();
    protected static final VoxelShape SHAPE_NW_T = Stream.of(
            Block.createCuboidShape(0, 15, 0, 16, 16, 16),
            Block.createCuboidShape(1, 14, 1, 16, 15, 16),
            Block.createCuboidShape(2, 13, 2, 16, 14, 16),
            Block.createCuboidShape(3, 12, 3, 16, 13, 16),
            Block.createCuboidShape(4, 11, 4, 16, 12, 16),
            Block.createCuboidShape(5, 10, 5, 16, 11, 16),
            Block.createCuboidShape(6, 9, 6, 16, 10, 16),
            Block.createCuboidShape(7, 8, 7, 16, 9, 16),
            Block.createCuboidShape(8, 7, 8, 16, 8, 16),
            Block.createCuboidShape(9, 6, 9, 16, 7, 16),
            Block.createCuboidShape(10, 5, 10, 16, 6, 16),
            Block.createCuboidShape(11, 4, 11, 16, 5, 16),
            Block.createCuboidShape(12, 3, 12, 16, 4, 16),
            Block.createCuboidShape(13, 2, 13, 16, 3, 16),
            Block.createCuboidShape(14, 1, 14, 16, 2, 16),
            Block.createCuboidShape(15, 0, 15, 16, 1, 16)
    ).reduce((v1, v2) -> VoxelShapes.combineAndSimplify(v1, v2, BooleanBiFunction.OR)).get();
    protected static final VoxelShape SHAPE_NE_T = Stream.of(
            Block.createCuboidShape(0, 15, 0, 16, 16, 16),
            Block.createCuboidShape(0, 14, 1, 15, 15, 16),
            Block.createCuboidShape(0, 13, 2, 14, 14, 16),
            Block.createCuboidShape(0, 12, 3, 13, 13, 16),
            Block.createCuboidShape(0, 11, 4, 12, 12, 16),
            Block.createCuboidShape(0, 10, 5, 11, 11, 16),
            Block.createCuboidShape(0, 9, 6, 10, 10, 16),
            Block.createCuboidShape(0, 8, 7, 9, 9, 16),
            Block.createCuboidShape(0, 7, 8, 8, 8, 16),
            Block.createCuboidShape(0, 6, 9, 7, 7, 16),
            Block.createCuboidShape(0, 5, 10, 6, 6, 16),
            Block.createCuboidShape(0, 4, 11, 5, 5, 16),
            Block.createCuboidShape(0, 3, 12, 4, 4, 16),
            Block.createCuboidShape(0, 2, 13, 3, 3, 16),
            Block.createCuboidShape(0, 1, 14, 2, 2, 16),
            Block.createCuboidShape(0, 0, 15, 1, 1, 16)
    ).reduce((v1, v2) -> VoxelShapes.combineAndSimplify(v1, v2, BooleanBiFunction.OR)).get();
    protected static final VoxelShape SHAPE_SW_T = Stream.of(
            Block.createCuboidShape(0, 15, 0, 16, 16, 16),
            Block.createCuboidShape(1, 14, 0, 16, 15, 15),
            Block.createCuboidShape(2, 13, 0, 16, 14, 14),
            Block.createCuboidShape(3, 12, 0, 16, 13, 13),
            Block.createCuboidShape(4, 11, 0, 16, 12, 12),
            Block.createCuboidShape(5, 10, 0, 16, 11, 11),
            Block.createCuboidShape(6, 9, 0, 16, 10, 10),
            Block.createCuboidShape(7, 8, 0, 16, 9, 9),
            Block.createCuboidShape(8, 7, 0, 16, 8, 8),
            Block.createCuboidShape(9, 6, 0, 16, 7, 7),
            Block.createCuboidShape(10, 5, 0, 16, 6, 6),
            Block.createCuboidShape(11, 4, 0, 16, 5, 5),
            Block.createCuboidShape(12, 3, 0, 16, 4, 4),
            Block.createCuboidShape(13, 2, 0, 16, 3, 3),
            Block.createCuboidShape(14, 1, 0, 16, 2, 2),
            Block.createCuboidShape(15, 0, 0, 16, 1, 1)
    ).reduce((v1, v2) -> VoxelShapes.combineAndSimplify(v1, v2, BooleanBiFunction.OR)).get();
    protected static final VoxelShape SHAPE_SE_T = Stream.of(
            Block.createCuboidShape(0, 15, 0, 16, 16, 16),
            Block.createCuboidShape(0, 14, 0, 15, 15, 15),
            Block.createCuboidShape(0, 13, 0, 14, 14, 14),
            Block.createCuboidShape(0, 12, 0, 13, 13, 13),
            Block.createCuboidShape(0, 11, 0, 12, 12, 12),
            Block.createCuboidShape(0, 10, 0, 11, 11, 11),
            Block.createCuboidShape(0, 9, 0, 10, 10, 10),
            Block.createCuboidShape(0, 8, 0, 9, 9, 9),
            Block.createCuboidShape(0, 7, 0, 8, 8, 8),
            Block.createCuboidShape(0, 6, 0, 7, 7, 7),
            Block.createCuboidShape(0, 5, 0, 6, 6, 6),
            Block.createCuboidShape(0, 4, 0, 5, 5, 5),
            Block.createCuboidShape(0, 3, 0, 4, 4, 4),
            Block.createCuboidShape(0, 2, 0, 3, 3, 3),
            Block.createCuboidShape(0, 1, 0, 2, 2, 2),
            Block.createCuboidShape(0, 0, 0, 1, 1, 1)
    ).reduce((v1, v2) -> VoxelShapes.combineAndSimplify(v1, v2, BooleanBiFunction.OR)).get();
    protected static final VoxelShape SHAPE_NW_I = Stream.of(
            Block.createCuboidShape(0, 0, 0, 16, 1, 16),
            Block.createCuboidShape(0, 1, 15, 16, 16, 16),
            Block.createCuboidShape(0, 1, 14, 15, 15, 15),
            Block.createCuboidShape(0, 1, 13, 14, 14, 14),
            Block.createCuboidShape(0, 1, 12, 13, 13, 13),
            Block.createCuboidShape(0, 1, 11, 12, 12, 12),
            Block.createCuboidShape(0, 1, 10, 11, 11, 11),
            Block.createCuboidShape(0, 1, 9, 10, 10, 10),
            Block.createCuboidShape(0, 1, 8, 9, 9, 9),
            Block.createCuboidShape(0, 1, 7, 8, 8, 8),
            Block.createCuboidShape(0, 1, 6, 7, 7, 7),
            Block.createCuboidShape(0, 1, 5, 6, 6, 6),
            Block.createCuboidShape(0, 1, 4, 5, 5, 5),
            Block.createCuboidShape(0, 1, 3, 4, 4, 4),
            Block.createCuboidShape(0, 1, 2, 3, 3, 3),
            Block.createCuboidShape(0, 1, 1, 2, 2, 2),
            Block.createCuboidShape(1, 1, 0, 2, 2, 1),
            Block.createCuboidShape(2, 1, 0, 3, 3, 2),
            Block.createCuboidShape(3, 1, 0, 4, 4, 3),
            Block.createCuboidShape(4, 1, 0, 5, 5, 4),
            Block.createCuboidShape(5, 1, 0, 6, 6, 5),
            Block.createCuboidShape(6, 1, 0, 7, 7, 6),
            Block.createCuboidShape(7, 1, 0, 8, 8, 7),
            Block.createCuboidShape(8, 1, 0, 9, 9, 8),
            Block.createCuboidShape(9, 1, 0, 10, 10, 9),
            Block.createCuboidShape(10, 1, 0, 11, 11, 10),
            Block.createCuboidShape(11, 1, 0, 12, 12, 11),
            Block.createCuboidShape(12, 1, 0, 13, 13, 12),
            Block.createCuboidShape(13, 1, 0, 14, 14, 13),
            Block.createCuboidShape(14, 1, 0, 15, 15, 14),
            Block.createCuboidShape(15, 1, 0, 16, 16, 15)
    ).reduce((v1, v2) -> VoxelShapes.combineAndSimplify(v1, v2, BooleanBiFunction.OR)).get();
    protected static final VoxelShape SHAPE_NE_I = Stream.of(
            Block.createCuboidShape(0, 0, 0, 16, 1, 16),
            Block.createCuboidShape(0, 1, 0, 1, 16, 16),
            Block.createCuboidShape(1, 1, 0, 2, 15, 15),
            Block.createCuboidShape(2, 1, 0, 3, 14, 14),
            Block.createCuboidShape(3, 1, 0, 4, 13, 13),
            Block.createCuboidShape(4, 1, 0, 5, 12, 12),
            Block.createCuboidShape(5, 1, 0, 6, 11, 11),
            Block.createCuboidShape(6, 1, 0, 7, 10, 10),
            Block.createCuboidShape(7, 1, 0, 8, 9, 9),
            Block.createCuboidShape(8, 1, 0, 9, 8, 8),
            Block.createCuboidShape(9, 1, 0, 10, 7, 7),
            Block.createCuboidShape(10, 1, 0, 11, 6, 6),
            Block.createCuboidShape(11, 1, 0, 12, 5, 5),
            Block.createCuboidShape(12, 1, 0, 13, 4, 4),
            Block.createCuboidShape(13, 1, 0, 14, 3, 3),
            Block.createCuboidShape(14, 1, 0, 15, 2, 2),
            Block.createCuboidShape(15, 1, 1, 16, 2, 2),
            Block.createCuboidShape(14, 1, 2, 16, 3, 3),
            Block.createCuboidShape(13, 1, 3, 16, 4, 4),
            Block.createCuboidShape(12, 1, 4, 16, 5, 5),
            Block.createCuboidShape(11, 1, 5, 16, 6, 6),
            Block.createCuboidShape(10, 1, 6, 16, 7, 7),
            Block.createCuboidShape(9, 1, 7, 16, 8, 8),
            Block.createCuboidShape(8, 1, 8, 16, 9, 9),
            Block.createCuboidShape(7, 1, 9, 16, 10, 10),
            Block.createCuboidShape(6, 1, 10, 16, 11, 11),
            Block.createCuboidShape(5, 1, 11, 16, 12, 12),
            Block.createCuboidShape(4, 1, 12, 16, 13, 13),
            Block.createCuboidShape(3, 1, 13, 16, 14, 14),
            Block.createCuboidShape(2, 1, 14, 16, 15, 15),
            Block.createCuboidShape(1, 1, 15, 16, 16, 16)
    ).reduce((v1, v2) -> VoxelShapes.combineAndSimplify(v1, v2, BooleanBiFunction.OR)).get();
    protected static final VoxelShape SHAPE_SW_I = Stream.of(
            Block.createCuboidShape(0, 0, 0, 16, 1, 16),
            Block.createCuboidShape(15, 1, 0, 16, 16, 16),
            Block.createCuboidShape(14, 1, 1, 15, 15, 16),
            Block.createCuboidShape(13, 1, 2, 14, 14, 16),
            Block.createCuboidShape(12, 1, 3, 13, 13, 16),
            Block.createCuboidShape(11, 1, 4, 12, 12, 16),
            Block.createCuboidShape(10, 1, 5, 11, 11, 16),
            Block.createCuboidShape(9, 1, 6, 10, 10, 16),
            Block.createCuboidShape(8, 1, 7, 9, 9, 16),
            Block.createCuboidShape(7, 1, 8, 8, 8, 16),
            Block.createCuboidShape(6, 1, 9, 7, 7, 16),
            Block.createCuboidShape(5, 1, 10, 6, 6, 16),
            Block.createCuboidShape(4, 1, 11, 5, 5, 16),
            Block.createCuboidShape(3, 1, 12, 4, 4, 16),
            Block.createCuboidShape(2, 1, 13, 3, 3, 16),
            Block.createCuboidShape(1, 1, 14, 2, 2, 16),
            Block.createCuboidShape(0, 1, 14, 1, 2, 15),
            Block.createCuboidShape(0, 1, 13, 2, 3, 14),
            Block.createCuboidShape(0, 1, 12, 3, 4, 13),
            Block.createCuboidShape(0, 1, 11, 4, 5, 12),
            Block.createCuboidShape(0, 1, 10, 5, 6, 11),
            Block.createCuboidShape(0, 1, 9, 6, 7, 10),
            Block.createCuboidShape(0, 1, 8, 7, 8, 9),
            Block.createCuboidShape(0, 1, 7, 8, 9, 8),
            Block.createCuboidShape(0, 1, 6, 9, 10, 7),
            Block.createCuboidShape(0, 1, 5, 10, 11, 6),
            Block.createCuboidShape(0, 1, 4, 11, 12, 5),
            Block.createCuboidShape(0, 1, 3, 12, 13, 4),
            Block.createCuboidShape(0, 1, 2, 13, 14, 3),
            Block.createCuboidShape(0, 1, 1, 14, 15, 2),
            Block.createCuboidShape(0, 1, 0, 15, 16, 1)
    ).reduce((v1, v2) -> VoxelShapes.combineAndSimplify(v1, v2, BooleanBiFunction.OR)).get();
    protected static final VoxelShape SHAPE_SE_I = Stream.of(
            Block.createCuboidShape(0, 0, 0, 16, 1, 16),
            Block.createCuboidShape(0, 1, 0, 16, 16, 1),
            Block.createCuboidShape(1, 1, 1, 16, 15, 2),
            Block.createCuboidShape(2, 1, 2, 16, 14, 3),
            Block.createCuboidShape(3, 1, 3, 16, 13, 4),
            Block.createCuboidShape(4, 1, 4, 16, 12, 5),
            Block.createCuboidShape(5, 1, 5, 16, 11, 6),
            Block.createCuboidShape(6, 1, 6, 16, 10, 7),
            Block.createCuboidShape(7, 1, 7, 16, 9, 8),
            Block.createCuboidShape(8, 1, 8, 16, 8, 9),
            Block.createCuboidShape(9, 1, 9, 16, 7, 10),
            Block.createCuboidShape(10, 1, 10, 16, 6, 11),
            Block.createCuboidShape(11, 1, 11, 16, 5, 12),
            Block.createCuboidShape(12, 1, 12, 16, 4, 13),
            Block.createCuboidShape(13, 1, 13, 16, 3, 14),
            Block.createCuboidShape(14, 1, 14, 16, 2, 15),
            Block.createCuboidShape(14, 1, 15, 15, 2, 16),
            Block.createCuboidShape(13, 1, 14, 14, 3, 16),
            Block.createCuboidShape(12, 1, 13, 13, 4, 16),
            Block.createCuboidShape(11, 1, 12, 12, 5, 16),
            Block.createCuboidShape(10, 1, 11, 11, 6, 16),
            Block.createCuboidShape(9, 1, 10, 10, 7, 16),
            Block.createCuboidShape(8, 1, 9, 9, 8, 16),
            Block.createCuboidShape(7, 1, 8, 8, 9, 16),
            Block.createCuboidShape(6, 1, 7, 7, 10, 16),
            Block.createCuboidShape(5, 1, 6, 6, 11, 16),
            Block.createCuboidShape(4, 1, 5, 5, 12, 16),
            Block.createCuboidShape(3, 1, 4, 4, 13, 16),
            Block.createCuboidShape(2, 1, 3, 3, 14, 16),
            Block.createCuboidShape(1, 1, 2, 2, 15, 16),
            Block.createCuboidShape(0, 1, 1, 1, 16, 16)
    ).reduce((v1, v2) -> VoxelShapes.combineAndSimplify(v1, v2, BooleanBiFunction.OR)).get();
    protected static final VoxelShape SHAPE_NW_I_T = Stream.of(
            Block.createCuboidShape(0, 15, 0, 16, 16, 16),
            Block.createCuboidShape(15, 0, 0, 16, 15, 16),
            Block.createCuboidShape(14, 1, 0, 15, 15, 15),
            Block.createCuboidShape(13, 2, 0, 14, 15, 14),
            Block.createCuboidShape(12, 3, 0, 13, 15, 13),
            Block.createCuboidShape(11, 4, 0, 12, 15, 12),
            Block.createCuboidShape(10, 5, 0, 11, 15, 11),
            Block.createCuboidShape(9, 6, 0, 10, 15, 10),
            Block.createCuboidShape(8, 7, 0, 9, 15, 9),
            Block.createCuboidShape(7, 8, 0, 8, 15, 8),
            Block.createCuboidShape(6, 9, 0, 7, 15, 7),
            Block.createCuboidShape(5, 10, 0, 6, 15, 6),
            Block.createCuboidShape(4, 11, 0, 5, 15, 5),
            Block.createCuboidShape(3, 12, 0, 4, 15, 4),
            Block.createCuboidShape(2, 13, 0, 3, 15, 3),
            Block.createCuboidShape(1, 14, 0, 2, 15, 2),
            Block.createCuboidShape(0, 14, 1, 1, 15, 2),
            Block.createCuboidShape(0, 13, 2, 2, 15, 3),
            Block.createCuboidShape(0, 12, 3, 3, 15, 4),
            Block.createCuboidShape(0, 11, 4, 4, 15, 5),
            Block.createCuboidShape(0, 10, 5, 5, 15, 6),
            Block.createCuboidShape(0, 9, 6, 6, 15, 7),
            Block.createCuboidShape(0, 8, 7, 7, 15, 8),
            Block.createCuboidShape(0, 7, 8, 8, 15, 9),
            Block.createCuboidShape(0, 6, 9, 9, 15, 10),
            Block.createCuboidShape(0, 5, 10, 10, 15, 11),
            Block.createCuboidShape(0, 4, 11, 11, 15, 12),
            Block.createCuboidShape(0, 3, 12, 12, 15, 13),
            Block.createCuboidShape(0, 2, 13, 13, 15, 14),
            Block.createCuboidShape(0, 1, 14, 14, 15, 15),
            Block.createCuboidShape(0, 0, 15, 15, 15, 16)
    ).reduce((v1, v2) -> VoxelShapes.combineAndSimplify(v1, v2, BooleanBiFunction.OR)).get();
    protected static final VoxelShape SHAPE_NE_I_T = Stream.of(
            Block.createCuboidShape(0, 15, 0, 16, 16, 16),
            Block.createCuboidShape(0, 0, 15, 16, 15, 16),
            Block.createCuboidShape(1, 1, 14, 16, 15, 15),
            Block.createCuboidShape(2, 2, 13, 16, 15, 14),
            Block.createCuboidShape(3, 3, 12, 16, 15, 13),
            Block.createCuboidShape(4, 4, 11, 16, 15, 12),
            Block.createCuboidShape(5, 5, 10, 16, 15, 11),
            Block.createCuboidShape(6, 6, 9, 16, 15, 10),
            Block.createCuboidShape(7, 7, 8, 16, 15, 9),
            Block.createCuboidShape(8, 8, 7, 16, 15, 8),
            Block.createCuboidShape(9, 9, 6, 16, 15, 7),
            Block.createCuboidShape(10, 10, 5, 16, 15, 6),
            Block.createCuboidShape(11, 11, 4, 16, 15, 5),
            Block.createCuboidShape(12, 12, 3, 16, 15, 4),
            Block.createCuboidShape(13, 13, 2, 16, 15, 3),
            Block.createCuboidShape(14, 14, 1, 16, 15, 2),
            Block.createCuboidShape(14, 14, 0, 15, 15, 1),
            Block.createCuboidShape(13, 13, 0, 14, 15, 2),
            Block.createCuboidShape(12, 12, 0, 13, 15, 3),
            Block.createCuboidShape(11, 11, 0, 12, 15, 4),
            Block.createCuboidShape(10, 10, 0, 11, 15, 5),
            Block.createCuboidShape(9, 9, 0, 10, 15, 6),
            Block.createCuboidShape(8, 8, 0, 9, 15, 7),
            Block.createCuboidShape(7, 7, 0, 8, 15, 8),
            Block.createCuboidShape(6, 6, 0, 7, 15, 9),
            Block.createCuboidShape(5, 5, 0, 6, 15, 10),
            Block.createCuboidShape(4, 4, 0, 5, 15, 11),
            Block.createCuboidShape(3, 3, 0, 4, 15, 12),
            Block.createCuboidShape(2, 2, 0, 3, 15, 13),
            Block.createCuboidShape(1, 1, 0, 2, 15, 14),
            Block.createCuboidShape(0, 0, 0, 1, 15, 15)
    ).reduce((v1, v2) -> VoxelShapes.combineAndSimplify(v1, v2, BooleanBiFunction.OR)).get();
    protected static final VoxelShape SHAPE_SW_I_T = Stream.of(
            Block.createCuboidShape(0, 15, 0, 16, 16, 16),
            Block.createCuboidShape(0, 0, 0, 16, 15, 1),
            Block.createCuboidShape(0, 1, 1, 15, 15, 2),
            Block.createCuboidShape(0, 2, 2, 14, 15, 3),
            Block.createCuboidShape(0, 3, 3, 13, 15, 4),
            Block.createCuboidShape(0, 4, 4, 12, 15, 5),
            Block.createCuboidShape(0, 5, 5, 11, 15, 6),
            Block.createCuboidShape(0, 6, 6, 10, 15, 7),
            Block.createCuboidShape(0, 7, 7, 9, 15, 8),
            Block.createCuboidShape(0, 8, 8, 8, 15, 9),
            Block.createCuboidShape(0, 9, 9, 7, 15, 10),
            Block.createCuboidShape(0, 10, 10, 6, 15, 11),
            Block.createCuboidShape(0, 11, 11, 5, 15, 12),
            Block.createCuboidShape(0, 12, 12, 4, 15, 13),
            Block.createCuboidShape(0, 13, 13, 3, 15, 14),
            Block.createCuboidShape(0, 14, 14, 2, 15, 15),
            Block.createCuboidShape(1, 14, 15, 2, 15, 16),
            Block.createCuboidShape(2, 13, 14, 3, 15, 16),
            Block.createCuboidShape(3, 12, 13, 4, 15, 16),
            Block.createCuboidShape(4, 11, 12, 5, 15, 16),
            Block.createCuboidShape(5, 10, 11, 6, 15, 16),
            Block.createCuboidShape(6, 9, 10, 7, 15, 16),
            Block.createCuboidShape(7, 8, 9, 8, 15, 16),
            Block.createCuboidShape(8, 7, 8, 9, 15, 16),
            Block.createCuboidShape(9, 6, 7, 10, 15, 16),
            Block.createCuboidShape(10, 5, 6, 11, 15, 16),
            Block.createCuboidShape(11, 4, 5, 12, 15, 16),
            Block.createCuboidShape(12, 3, 4, 13, 15, 16),
            Block.createCuboidShape(13, 2, 3, 14, 15, 16),
            Block.createCuboidShape(14, 1, 2, 15, 15, 16),
            Block.createCuboidShape(15, 0, 1, 16, 15, 16)
    ).reduce((v1, v2) -> VoxelShapes.combineAndSimplify(v1, v2, BooleanBiFunction.OR)).get();
    protected static final VoxelShape SHAPE_SE_I_T = Stream.of(
            Block.createCuboidShape(0, 15, 0, 16, 16, 16),
            Block.createCuboidShape(0, 0, 0, 1, 15, 16),
            Block.createCuboidShape(1, 1, 1, 2, 15, 16),
            Block.createCuboidShape(2, 2, 2, 3, 15, 16),
            Block.createCuboidShape(3, 3, 3, 4, 15, 16),
            Block.createCuboidShape(4, 4, 4, 5, 15, 16),
            Block.createCuboidShape(5, 5, 5, 6, 15, 16),
            Block.createCuboidShape(6, 6, 6, 7, 15, 16),
            Block.createCuboidShape(7, 7, 7, 8, 15, 16),
            Block.createCuboidShape(8, 8, 8, 9, 15, 16),
            Block.createCuboidShape(9, 9, 9, 10, 15, 16),
            Block.createCuboidShape(10, 10, 10, 11, 15, 16),
            Block.createCuboidShape(11, 11, 11, 12, 15, 16),
            Block.createCuboidShape(12, 12, 12, 13, 15, 16),
            Block.createCuboidShape(13, 13, 13, 14, 15, 16),
            Block.createCuboidShape(14, 14, 14, 15, 15, 16),
            Block.createCuboidShape(15, 14, 14, 16, 15, 15),
            Block.createCuboidShape(14, 13, 13, 16, 15, 14),
            Block.createCuboidShape(13, 12, 12, 16, 15, 13),
            Block.createCuboidShape(12, 11, 11, 16, 15, 12),
            Block.createCuboidShape(11, 10, 10, 16, 15, 11),
            Block.createCuboidShape(10, 9, 9, 16, 15, 10),
            Block.createCuboidShape(9, 8, 8, 16, 15, 9),
            Block.createCuboidShape(8, 7, 7, 16, 15, 8),
            Block.createCuboidShape(7, 6, 6, 16, 15, 7),
            Block.createCuboidShape(6, 5, 5, 16, 15, 6),
            Block.createCuboidShape(5, 4, 4, 16, 15, 5),
            Block.createCuboidShape(4, 3, 3, 16, 15, 4),
            Block.createCuboidShape(3, 2, 2, 16, 15, 3),
            Block.createCuboidShape(2, 1, 1, 16, 15, 2),
            Block.createCuboidShape(1, 0, 0, 16, 15, 1)
    ).reduce((v1, v2) -> VoxelShapes.combineAndSimplify(v1, v2, BooleanBiFunction.OR)).get();

}
